package com.suray.wcs.service.system.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Sets;
import com.suray.basic.wcs.node.WCSNodeModule;
import com.suray.basic.wcs.node.bo.WCSNodeModuleBO;
import com.suray.basic.wcs.path.bo.ConnectPlanBO;
import com.suray.basic.wcs.path.bo.NodeAndPlanInfoBO;
import com.suray.basic.wcs.path.core.Lifter;
import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.path.core.api.impl.NodeChooseImpl;
import com.suray.basic.wcs.path.core.api.impl.RouteFactoryImp;
import com.suray.basic.wcs.path.enums.Direction;
import com.suray.basic.wcs.path.enums.NodeStatus;
import com.suray.basic.wcs.path.enums.NodeType;
import com.suray.basic.wcs.path.properties.PathProperties;
import com.suray.basic.wcs.plc.core.Plc;
import com.suray.basic.wcs.plc.entity.PlcDeviceDB;
import com.suray.basic.wcs.plc.enums.Location;
import com.suray.basic.wcs.plc.enums.Network;
import com.suray.basic.wcs.plc.enums.PlcDeviceType;
import com.suray.basic.wcs.plc.mapper.PlcDeviceDBMapper;
import com.suray.basic.wcs.plc.pojo.DeviceBase;
import com.suray.basic.wcs.plc.pojo.oper.lifter.LifterOper;
import com.suray.basic.wcs.plc.pojo.oper.StationOper;
import com.suray.basic.wcs.plc.pojo.oper.lifter.ForkLifterOper;
import com.suray.basic.wcs.plc.pojo.oper.single.DeviceInterface;
import com.suray.basic.wcs.plc.utils.PlcExecuteException;
import com.suray.basic.wcs.plc.utils.PlcUnit;
import com.suray.basic.wcs.utils.Coord;
import com.suray.basic.wcs.utils.LockNode;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.WcsFuncUtil;
import com.suray.commin.core.utils.StringUtils;
import com.suray.commin.enmus.DeviceStatus;
import com.suray.wcs.res.enums.RgvType;
import com.suray.wcs.service.core.util.LockNodeManager;
import com.suray.wcs.service.enums.PlusType;
import com.suray.wcs.service.enums.PortModel;
import com.suray.wcs.service.enums.RoadType;
import com.suray.wcs.service.enums.TaskType;
import com.suray.wcs.service.init.SystemInit;
import com.suray.wcs.service.pojo.NodeInfo;
import com.suray.wcs.service.pojo.Rgv;
import com.suray.wcs.service.system.bo.ConnectNodeBO;
import com.suray.wcs.service.system.bo.NodeAndPlanBO;
import com.suray.wcs.service.system.bo.ResourcesLockNodeBO;
import com.suray.wcs.service.system.bo.ResourcesNodeBO;
import com.suray.wcs.service.system.entity.*;
import com.suray.wcs.service.system.mapper.NodeDBMapper;
import com.suray.wcs.service.system.mapper.PlcDBMapper;
import com.suray.wcs.service.system.service.INodeDBService;
import com.suray.wcs.service.util.NodeDirection;
import com.suray.wcs.service.util.WcsUtil;
import com.suray.wcs.service.websocket.NodeInfoPush;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author dean
 * 2019年9月27日 上午11:05:21
 */
@Service
public class NodeDBServiceImpl extends ServiceImpl<NodeDBMapper, NodeDB> implements INodeDBService {

    @Resource
    private PlcDeviceDBMapper plcdevicedbMapper;

    @Autowired
    private TaskDBServiceImpl taskService;
    @Autowired
    private NodeDBMapper nodeMapper;
    @Autowired
    private PlcDBMapper plcDBMapper;

    private Integer codeFlage=0;

    public Integer getCodeFlage() {
        return codeFlage;
    }

    public void setCodeFlage(Integer codeFlage) {
        this.codeFlage = codeFlage;
    }

    /**
     * 查找特定属性的节点
     *
     * @param nodeType
     * @param nodeStatus
     * @param floor
     * @return 满足条件的所有节点
     */
    @Override
    public List<Coord> findSpecifiedNodes(NodeType nodeType, NodeStatus nodeStatus, int floor) {
        List<Coord> specifiedCoord = new ArrayList<Coord>();
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridType, nodeType).eq(NodeDB::getGridz, floor).eq(NodeDB::getCanToStop, true);
        return getCoordList(nodeStatus, specifiedCoord, queryWrapper);
    }

    public static void changeFBGStringToInt(NodeDB nodedb) {
        try {
            String forbidRgvs = nodedb.getForbidRgvs();

            if (StringUtils.isNotEmpty(forbidRgvs) && forbidRgvs.contains("/")) {
                String[] rgvTypes = forbidRgvs.split("/");
                String rgvT = String.valueOf(RgvType.parseDes(rgvTypes[0]).getValue());
                for (int i = 1; i < rgvTypes.length; i++) {
                    String rgvType = rgvTypes[i];
                    rgvT += "-" + RgvType.parseDes(rgvType).getValue();
                }
                nodedb.setForbidRgvs(rgvT);
            }
        } catch (Exception e) {
            WcsFuncUtil.exceptionLog(e, NodeDBServiceImpl.class);
        }
    }

    public static void changeFBGIntToString(NodeDB nodedb) {
        try {
            String forbidRgvs = nodedb.getForbidRgvs();

            if (StringUtils.isNotEmpty(forbidRgvs) && forbidRgvs.contains("-")) {
                String[] rgvTypes = forbidRgvs.split("-");
                String rgvT = "";
                for (int i = 0; i < rgvTypes.length; i++) {
                    rgvT += RgvType.parseState(Integer.valueOf(rgvTypes[i])).toString() + "/";
                }
                nodedb.setForbidRgvs(rgvT);
            }
        } catch (Exception e) {
            WcsFuncUtil.exceptionLog(e, NodeDBServiceImpl.class);
        }
    }

    /**
     * 获取所有货位的节点信息
     *
     * @param floor
     */
    public List<NodeDB> findCargoNodes(int floor) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridz, floor)
                .eq(NodeDB::getGridType, NodeType.P);
        List<NodeDB> nodeDBList = list(queryWrapper);
        List<NodeDB> nodeDBList1 = new ArrayList<>();

        boolean checkXY = true;
        if (nodeDBList.get(0).getCanToUp() || nodeDBList.get(0).getCanToDown()) {
            for (int y = 1; y <= nodeMapper.getMaxY(); y++) {
                queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().eq(NodeDB::getGridz, floor)
                        .eq(NodeDB::getGridType, NodeType.P)
                        .eq(NodeDB::getGridy, y);
                nodeDBList = list(queryWrapper);

                if (nodeDBList != null || nodeDBList.size() != 0) {
                    if (!checkXY) {
                        Collections.reverse(nodeDBList);
                    }
                    nodeDBList1.addAll(nodeDBList);
                    checkXY = !checkXY;
                }
            }
        } else {
            for (int x = 1; x <= nodeMapper.getMaxX(); x++) {
                queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().eq(NodeDB::getGridz, floor)
                        .eq(NodeDB::getGridType, NodeType.P)
                        .eq(NodeDB::getGridx, x);
                nodeDBList = list(queryWrapper);
                if (nodeDBList != null || nodeDBList.size() != 0) {
                    if (!checkXY) {
                        Collections.reverse(nodeDBList);
                    }
                    nodeDBList1.addAll(nodeDBList);
                    checkXY = !checkXY;
                }
            }
        }
        return nodeDBList1;
    }

    /**
     * 查找特定属性的节点
     *
     * @param nodeTypeList
     * @param nodeStatus
     * @param floor
     * @return 满足条件的所有节点
     */
    @Override
    public List<Coord> findSpecifiedNodes(List<NodeType> nodeTypeList, NodeStatus nodeStatus, int floor) {
        List<Coord> specifiedCoord = new ArrayList<Coord>();
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(NodeDB::getGridType, nodeTypeList).eq(NodeDB::getGridz, floor).eq(NodeDB::getCanToStop, true);
        return getCoordList(nodeStatus, specifiedCoord, queryWrapper);
    }

    @Override
    public boolean saveBatch(Collection<NodeDB> entityList) {
        List<NodeDB> entityList1 = (List<NodeDB>) entityList;
        if (entityList.size() < 100) {
            nodeMapper.saveBatch(entityList1);
        } else {
            int preInsertDataCount = 100;
            // 可遍历的插入数据库的次数
            int insertSqlCount = 0;
            // 总数据条数
            int totalDataCount = entityList1.size();
            if (totalDataCount % preInsertDataCount == 0) {
                insertSqlCount = totalDataCount / preInsertDataCount;
            } else {
                insertSqlCount = totalDataCount / preInsertDataCount + 1;
            }
            for (int i = 0; i < insertSqlCount; i++) {
                int startNumber = i * preInsertDataCount;
                int endUnmber = (i + 1) * preInsertDataCount;
                if (endUnmber > totalDataCount) {
                    endUnmber = totalDataCount;
                }
                List<NodeDB> subListOK = entityList1.subList(startNumber, endUnmber);
                nodeMapper.saveBatch(subListOK);
            }
        }
        return true;
    }

    /**
     * 查找特定属性的节点
     *
     * @param nodeType
     * @param nodeStatus
     * @param floor
     * @return 满足条件的所有节点
     */
    @Override
    public List<Coord> findSpecifiedNodesByRoadType(NodeType nodeType, NodeStatus nodeStatus, int floor, String roadType) {
        List<Coord> specifiedCoord = new ArrayList<Coord>();
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridType, nodeType)
                .eq(NodeDB::getGridz, floor)
                .eq(NodeDB::getRoadType, roadType)
                .eq(NodeDB::getCanToStop, true);
        return getCoordList(nodeStatus, specifiedCoord, queryWrapper);
    }

    /**
     * 获取坐标集合
     *
     * @param nodeStatus     节点状态
     * @param specifiedCoord 集合
     * @param queryWrapper   查询条件
     * @return
     */
    private List<Coord> getCoordList(NodeStatus nodeStatus, List<Coord> specifiedCoord, QueryWrapper<NodeDB> queryWrapper) {
        if (nodeStatus != null) {
            queryWrapper.lambda().eq(NodeDB::getGridStatus, nodeStatus);
        }
        List<NodeDB> resultList = list(queryWrapper);
        for (NodeDB nodedb : resultList) {
            specifiedCoord.add(new Coord(nodedb.getGridx(), nodedb.getGridy(), nodedb.getGridz()));
        }
        return specifiedCoord;
    }

    /**
     * 获取仓库中所有库区以及对应的层数
     */
    @Override
    public List<NodeDB> getRegion(String xyz) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        if ("gridx".equals(xyz)) {
            queryWrapper.select("DISTINCT gridx");
        } else if ("gridy".equals(xyz)) {
            queryWrapper.select("DISTINCT gridy");
        } else if ("gridz".equals(xyz)) {
            queryWrapper.select("DISTINCT gridz");
        }
        return list(queryWrapper);
    }

    /**
     * 获取固定层的所有节点
     *
     * @return
     */
    @Override
    public List<NodeDB> getNodeByFloor(Integer floor) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridz, floor);
        return list(queryWrapper);
    }

    /**
     * 获取当前仓库的所有节点
     *
     * @return
     */
    @Override
    public List<NodeDB> getNodeDBList() {
        return list();
    }

    @Override
    public Node getNodeByGroundCode(String groundCode) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGroundCode, groundCode);
        List<NodeDB> list = list(queryWrapper);

        if (list.size() == 0) {
            return null;
        } else if (list.size() > 1) {
            throw new RuntimeException("通过地码找到多个节点：" + WcsUtil.listToString(list));
        }

        return new Node(list.get(0).getGridx(), list.get(0).getGridy(), list.get(0).getGridz());
    }

    /**
     * 获取当前库区当前层的所有路径规划节点
     *
     * @return
     */
    @Override
    public List<Node> getNodeListByFloor(Integer floor) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridz, floor);
        List<NodeDB> list = list(queryWrapper);
        List<Node> nodes = new ArrayList<>();
        for (NodeDB nodedb : list) {
            Node node = new Node(
                    new Coord(nodedb.getGridx(), nodedb.getGridy(), nodedb.getGridz()),
                    NodeType.parseNodeType(nodedb.getGridType().getCode()),
                    NodeStatus.parse(nodedb.getGridStatus()),
                    nodedb.getCanToUp(),
                    nodedb.getCanToDown(),
                    nodedb.getCanToLeft(),
                    nodedb.getCanToRight(),
                    nodedb.getCanToStop(),
                    nodedb.getIsDoor(),
                    nodedb.getRgvPoint(),
                    nodedb.getTag(),
                    nodedb.getForkHeight(),
                    nodedb.getForbidRgvs()
            );
            nodes.add(node);
        }
        return nodes;
    }

    /**
     * 获取所有路径规划节点
     *
     * @return
     */
    @Override
    public List<Node> getNodeList() {
        List<NodeDB> list = this.getNodeDBList();
        List<Node> nodes = new ArrayList<>();
        return convertNodeDBToNode(list, nodes);
    }

    @Override
    public List<ResourcesLockNodeBO> getLockNodes(Integer floor) {
        List<ResourcesLockNodeBO> resourcesLockNodeBOS = new ArrayList<>();
        Set<LockNode> lockNodeSet = LockNodeManager.getLockedNodes();
        lockNodeSet.forEach(x -> {
            if (x.getZ() == floor.intValue()) {
                ResourcesLockNodeBO resourcesLockNodeBO = new ResourcesLockNodeBO();
                resourcesLockNodeBO.setX(x.getX());
                resourcesLockNodeBO.setY(x.getY());
                resourcesLockNodeBO.setZ(x.getZ());
                resourcesLockNodeBO.setUnLockTime(x.getUnLockTime());
                if (x.getLockedBy() instanceof Rgv) {
                    Rgv rgv = (Rgv) x.getLockedBy();
                    resourcesLockNodeBO.setLockBy(rgv.getRgvName());
                    resourcesLockNodeBO.setLockNo(rgv.getRgvDB().getRgvNo().toString());
                    if (rgv.getCurLocation() != null &&
                            rgv.getCurLocation().getX() == x.getX() &&
                            rgv.getCurLocation().getY() == x.getY() &&
                            rgv.getCurLocation().getZ() == x.getZ()) {
                        resourcesLockNodeBO.setShowType(ResourcesLockNodeBO.ShowTypeEnum.RGV.name());
                    } else if (rgv.getRgvDB().getLocationNode() != null &&
                            rgv.getRgvDB().getLocationNode().getX() == x.getX() &&
                            rgv.getRgvDB().getLocationNode().getY() == x.getY() &&
                            rgv.getRgvDB().getLocationNode().getZ() == x.getZ()) {
                        resourcesLockNodeBO.setShowType(ResourcesLockNodeBO.ShowTypeEnum.RGV.name());
                    }
                } else if (x.getLockedBy() instanceof TaskDB) {
                    resourcesLockNodeBO.setShowType(ResourcesLockNodeBO.ShowTypeEnum.TASK_DB.name());
                    TaskDB taskdb = (TaskDB) x.getLockedBy();
                    resourcesLockNodeBO.setLockBy("任务" + taskdb.getTaskId());
                    resourcesLockNodeBO.setLockNo(taskdb.getTaskId().toString());
                }

                resourcesLockNodeBOS.add(resourcesLockNodeBO);
            }
        });
        return resourcesLockNodeBOS;
    }

    @Override
    public List<ResourcesNodeBO> getResourcesNodes(Integer floor) {
        List<ResourcesNodeBO> resourcesNodeBOS = new ArrayList<>();
        Map<String, ResourcesNodeBO> resourcesNodeBOMap = new HashMap<>();
        Set<LockNode> lockNodeSet = LockNodeManager.getLockedNodes();
        lockNodeSet.forEach(x -> {
            if (x.getZ() == floor.intValue()) {
                ResourcesNodeBO resourcesNodeBO = new ResourcesNodeBO(x.getX(), x.getY(), x.getZ());
                resourcesNodeBO.setUnLockTime(x.getUnLockTime());
                if (x.getLockedBy() instanceof Rgv) {
                    Rgv rgv = (Rgv) x.getLockedBy();
                    resourcesNodeBO.setLockBy(rgv.getRgvName());
                    resourcesNodeBO.setLockNo(rgv.getRgvDB().getRgvNo().toString());
                    if (rgv.getCurLocation() != null &&
                            rgv.getCurLocation().getX() == x.getX() &&
                            rgv.getCurLocation().getY() == x.getY() &&
                            rgv.getCurLocation().getZ() == x.getZ()) {
                        resourcesNodeBO.setShowType(ResourcesLockNodeBO.ShowTypeEnum.RGV.name());
                    } else if (rgv.getRgvDB().getLocationNode() != null &&
                            rgv.getRgvDB().getLocationNode().getX() == x.getX() &&
                            rgv.getRgvDB().getLocationNode().getY() == x.getY() &&
                            rgv.getRgvDB().getLocationNode().getZ() == x.getZ()) {
                        resourcesNodeBO.setShowType(ResourcesLockNodeBO.ShowTypeEnum.RGV.name());
                    }
                } else if (x.getLockedBy() instanceof TaskDB) {
                    resourcesNodeBO.setShowType(ResourcesLockNodeBO.ShowTypeEnum.TASK_DB.name());
                    TaskDB taskdb = (TaskDB) x.getLockedBy();
                    resourcesNodeBO.setLockBy("任务" + taskdb.getTaskId());
                    resourcesNodeBO.setLockNo(taskdb.getTaskId().toString());
                } else {
                    Object obj = x.getLockedBy();
                    resourcesNodeBO.setLockBy(obj.toString());
                    resourcesNodeBO.setLockNo(obj.toString());
                }
                resourcesNodeBOMap.put(resourcesNodeBO.toString(), resourcesNodeBO);
                resourcesNodeBOS.add(resourcesNodeBO);
            }
        });
        WCSNodeModule.wcsNodeModuleMapCache.entrySet().stream().forEach(item -> {
            WCSNodeModuleBO wcsNodeModuleBO = item.getValue();
            if (wcsNodeModuleBO.getZ() == floor && wcsNodeModuleBO.getRouteBy().size() > 0) {
                ResourcesNodeBO resourcesNodeBO = resourcesNodeBOMap.get(item.getKey());
                if (resourcesNodeBO == null) {
                    resourcesNodeBO = new ResourcesNodeBO(wcsNodeModuleBO.getX(), wcsNodeModuleBO.getY(), wcsNodeModuleBO.getZ());
                }
                List<Integer> routeByIds = new ArrayList<>();
                wcsNodeModuleBO.getRouteBy().entrySet().forEach(routeBy -> {
                    routeByIds.add(routeBy.getValue().getKey());
                });
                if (routeByIds.size() > 0) {
                    resourcesNodeBO.setRouteDirection(wcsNodeModuleBO.getRouteBy().get(routeByIds.get(0).toString()).getRouteDirection());
                }
                if (wcsNodeModuleBO.getMaxLockTimeWCSNodeModuleRouteBO() != null) {
                    resourcesNodeBO.setLockTime(wcsNodeModuleBO.getMaxLockTimeWCSNodeModuleRouteBO().getLockTime());
                }
                resourcesNodeBO.setRouteBy(routeByIds);
                resourcesNodeBOMap.put(resourcesNodeBO.toString(), resourcesNodeBO);
                resourcesNodeBOS.add(resourcesNodeBO);
            }
        });
        return resourcesNodeBOS;
    }

    @Override
    public void unLockNode(Coord coord) {
        List<Rgv> rgvs = SystemInit.getRgvList();
        for (Rgv rgv : rgvs) {
            Node node = rgv.getRgvDB().getLocationNode();
            if (node != null && node.getX() == coord.getX() &&
                    node.getY() == coord.getY() && node.getZ() == coord.getZ()) {
                throw new IllegalStateException("小车当前位置不能解锁");
            }
        }
        LockNodeManager.removeLockedNode(coord);
    }

    private List<Node> getNodes(List<NodeDB> list, List<Node> nodes) {
        return convertNodeDBToNode(list, nodes);
    }

    /**
     * nodeDB集合转换成node集合
     * @param nodeDBList
     * @param nodes
     * @return
     */
    private List<Node> convertNodeDBToNode(List<NodeDB> nodeDBList, List<Node> nodes) {
        for (NodeDB nodedb : nodeDBList) {
            Node node = new Node(
                    new Coord(nodedb.getGridx(), nodedb.getGridy(), nodedb.getGridz()),
                    NodeType.parseNodeType(nodedb.getGridType().getCode()),
                    NodeStatus.parse(nodedb.getGridStatus()),
                    nodedb.getCanToUp(),
                    nodedb.getCanToDown(),
                    nodedb.getCanToLeft(),
                    nodedb.getCanToRight(),
                    nodedb.getCanToStop(),
                    nodedb.getIsDoor(),
                    nodedb.getRgvPoint(),
                    nodedb.getTag(),
                    nodedb.getForkHeight(),
                    nodedb.getForbidRgvs()
            );
            node.setGroundCode(nodedb.getTag());
            node.setOffsetX(nodedb.getOffsetx());
            node.setOffsetY(nodedb.getOffsety());
            node.setSonRgvGroundCode(nodedb.getGroundCode());
            node.setSeerMapX(nodedb.getSeerMapX());
            node.setSeerMapY(nodedb.getSeerMapY());
            nodes.add(node);
        }
        return nodes;
    }

    @Override
    public List<Node> getNodeByLayer(List<Integer> layers) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(NodeDB::getGridz, layers);
        List<NodeDB> list = list(queryWrapper);
        List<Node> nodes = new ArrayList<>();
        return getNodes(list, nodes);
    }

    @Override
    public List<NodeDB> getNodeDbNeedPrintCodeByX() {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().notIn(NodeDB::getGridType, Arrays.asList(NodeType.CONNECTION, NodeType.L, NodeType.ST, NodeType.B)).orderByAsc(NodeDB::getGridz).orderByAsc(NodeDB::getGridy).orderByAsc(NodeDB::getGridx);
        return list(queryWrapper);
    }

    @Override
    public List<NodeDB> getNodeDbNeedPrintCodeByY() {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().notIn(NodeDB::getGridType, Arrays.asList(NodeType.CONNECTION, NodeType.L, NodeType.ST, NodeType.B)).orderByAsc(NodeDB::getGridz).orderByAsc(NodeDB::getGridx).orderByAsc(NodeDB::getGridy);
        return list(queryWrapper);
    }

    /**
     * 获取当前库区的提升机
     *
     * @return
     */
    @Override
    public Set<Lifter> getLiftSet() {
        Set<Lifter> lifterSet = new HashSet<>();
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridType, NodeType.L.getCode()).eq(NodeDB::getGridz, 1);
        List<NodeDB> resultList = list(queryWrapper);
        for (NodeDB nodedb : resultList) {
            lifterSet.add(new Lifter(nodedb.getGridx(), nodedb.getGridy()));
        }
        return lifterSet;
    }


    /**
     * @return
     * @Description: 查询去重后的库口坐标
     */
    @Override
    public List<NodeDB> getNodeDBByType() {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(NodeDB::getGridType, Arrays.asList(NodeType.CONNECTION, NodeType.L));
        List<NodeDB> nodeDBS = list(queryWrapper);
        return nodeDBS;
    }

    /**
     * 通过坐标获取DB对象
     *
     * @param coord
     * @return
     */
    @Override
    public NodeDB getNodeDB(Coord coord) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridx, coord.getX()).eq(NodeDB::getGridy, coord.getY()).eq(NodeDB::getGridz,
                coord.getZ());
        return this.getOne(queryWrapper);
    }

    /**
     * 更新货位是否有货状态 并推送至前端显示
     * @param coord
     * @param gridStatus
     * @return
     */
    @Override
    public boolean updateNodeDBStatus(Coord coord, String gridStatus) {
        UpdateWrapper<NodeDB> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(NodeDB::getGridx, coord.getX()).eq(NodeDB::getGridy, coord.getY()).eq(NodeDB::getGridz,
                coord.getZ()).set(NodeDB::getGridStatus, gridStatus);
        boolean result = this.update(updateWrapper);
        if (result) {
            NodeDB nodedb = getNodeDB(coord);
            NodeInfo nodeInfo = new NodeInfo();
            BeanUtils.copyProperties(nodedb, nodeInfo);
            NodeInfoPush.push(nodeInfo);
        }
        return result;
    }

    @Override
    public boolean updateNodeDBRgv60Map(List<Node> nodes) {
        List<NodeDB> nodeDBS = list();
        List<NodeDB> updateNodes = new ArrayList<>();
        nodes.forEach(node -> {
            for (NodeDB nodeDB : nodeDBS) {
                if (node.getX() == nodeDB.getGridx() && node.getY() == nodeDB.getGridy()
                        && node.getZ() == nodeDB.getGridz()) {
                    nodeDB.setRgvPoint(node.getRgv60Point());
                    nodeDB.setTag(node.getGroundCode());
                    nodeDB.setOffsetx(node.getOffsetX());
                    nodeDB.setOffsety(node.getOffsetY());
                    nodeDB.setSeerMapX(node.getSeerMapX());
                    nodeDB.setSeerMapY(node.getSeerMapY());
                    updateNodes.add(nodeDB);
                }
            }
        });

        updateBatchById(updateNodes, updateNodes.size());

        return false;
    }

    /**
     * 更新货位的托盘码
     * @param coord
     * @param PalletCode
     * @return
     */
    @Override
    public boolean updateNodeDBBarCode(Coord coord, String PalletCode) {
        UpdateWrapper<NodeDB> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(NodeDB::getGridx, coord.getX()).eq(NodeDB::getGridy, coord.getY()).eq(NodeDB::getGridz,
                coord.getZ()).set(NodeDB::getPalletCode, PalletCode);
        return this.update(updateWrapper);
    }

    @Override
    public Node getStartIsOutNodeForSingle(Node start) {
        Node node = null;
        NodeDB nodeDB = this.getNodeDB(new Coord(start.getX(), start.getY(), start.getZ()));
        // 如果是单巷道，则根据深位划分，找出最浅有货位置
        String singleNo = nodeDB.getRoadNo();
        //如果没有划分单巷道则直接返回
        if (org.apache.commons.lang3.StringUtils.isBlank(singleNo)) {
            return start;
        }
        String singleOrder = nodeDB.getSingleOrder();

        List<NodeDB> nodeDBYList = this.getNodeDBListBySingle(singleNo, singleOrder, NodeStatus.Y.name());
        if (CollectionUtils.isNotEmpty(nodeDBYList)) {
            node = buildNode(nodeDBYList.get(0));
        }
        return node;
    }

    private Node buildNode(NodeDB nodeDB) {
        return new Node(
                new Coord(nodeDB.getGridx(), nodeDB.getGridy(), nodeDB.getGridz())
        );
    }

    /**
     * 获取当前巷道有货/无货的货位列表
     *
     * @param singleNo
     * @param singleOrder
     * @param nodeStatus
     * @return
     */
    @Override
    public List<NodeDB> getNodeDBListBySingle(String singleNo, String singleOrder, String nodeStatus) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<NodeDB>();
        queryWrapper.lambda()
                .eq(NodeDB::getRoadNo, singleNo)
                .in(NodeDB::getGridType, NodeType.P,NodeType.CONNECTION);
        if (org.apache.commons.lang3.StringUtils.isNotBlank(nodeStatus)) {
            queryWrapper.lambda().eq(NodeDB::getGridStatus, nodeStatus);
        }
        if (singleOrder.equals(Direction.UP.getCode())) {
            queryWrapper.lambda().orderByDesc(NodeDB::getGridx);
        } else if (singleOrder.equals(Direction.DOWN.getCode())) {
            queryWrapper.lambda().orderByAsc(NodeDB::getGridx);
        } else if (singleOrder.equals(Direction.LEFT.getCode())) {
            queryWrapper.lambda().orderByDesc(NodeDB::getGridy);
        } else if (singleOrder.equals(Direction.RIGHT.getCode())) {
            queryWrapper.lambda().orderByAsc(NodeDB::getGridy);
        }
        return list(queryWrapper);
    }

    /**
     * 更新提升机左右接驳点是否有货信息
     */
    @Override
    public void updateLifterLeftOrRightNodeDB(Coord coord, String gridStatus) {
        NodeDB nodeDB = getNodeDB(coord);
        if (nodeDB != null && NodeType.CONNECTION.equals(nodeDB.getGridType())) {
            LifterOper lifterOper = (LifterOper) Plc.deviceLifterCoordMap.get(new Coord(coord.getX(), coord.getY(), 1));
            if (lifterOper != null && (Location.LEFT.equals(lifterOper.getLocation()) || Location.RIGHT.equals(lifterOper.getLocation()))) {
                this.updateNodeDBStatus(coord, gridStatus);
            }
        }
    }

    /**
     * 通过WMS货位编码获取DB对象
     *
     * @param gridId
     * @return
     */
    @Override
    public NodeDB getNodeDBByGridID(String gridId) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridId, gridId);
        return this.getOne(queryWrapper);
    }

    /**
     * 获取提升机
     *
     * @param node 节点位置
     * @return
     */
    public Lifter getLifter(Node node) {
        Set<Lifter> lifters = getLiftSet();
        for (Lifter lifter : lifters) {
            if (lifter.getX() == node.getX() && lifter.getY() == node.getY()) {
                return lifter;
            }
        }
        throw new IllegalArgumentException("该节点不是提升机位置");
    }

    /**
     * 通过路径动作获取提升机
     *
     * @param node 路径
     * @return
     */
    public Lifter getLifterByPathAction(Node node) {
        try {
            return getLifter(new Node(node));
        } catch (Exception e) {
            throw new IllegalStateException("该道路上无提升机");
        }
    }


    public NodeDB getNodeDB(Node node) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<NodeDB>();
        queryWrapper.lambda()
                .eq(NodeDB::getGridx, node.getX())
                .eq(NodeDB::getGridy, node.getY())
                .eq(NodeDB::getGridz, node.getZ());
        List<NodeDB> list = list(queryWrapper);
        return list.size() > 0 ? list.get(0) : null;
    }

    @Override
    public List<Node> getNodeByType(NodeType nodeType) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridType, nodeType);
        List<NodeDB> list = list(queryWrapper);
        List<Node> nodes = new ArrayList<>();
        for (NodeDB nodedb : list) {
            Node node = new Node(
                    new Coord(nodedb.getGridx(), nodedb.getGridy(), nodedb.getGridz()),
                    NodeType.parseNodeType(nodedb.getGridType().getCode()),
                    NodeStatus.parse(nodedb.getGridStatus()),
                    nodedb.getCanToUp(),
                    nodedb.getCanToDown(),
                    nodedb.getCanToLeft(),
                    nodedb.getCanToRight(),
                    nodedb.getCanToStop(),
                    nodedb.getIsDoor(),
                    nodedb.getRgvPoint(),
                    nodedb.getTag(),
                    nodedb.getForkHeight(),
                    nodedb.getForbidRgvs()
            );
            nodes.add(node);
        }
        return nodes;
    }


    @Override
    public int getNodePUsedNum() {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridType, NodeType.P).eq(NodeDB::getGridStatus, "Y");
        return count(queryWrapper);
    }

    @Override
    public NodeDB findByBarcode(String barcode) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGroundCode, barcode).orderByAsc(NodeDB::getNodeId);
        List<NodeDB> list = list(queryWrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public List<NodeDB> getNodeDBByFloor(Integer floor) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getGridz, floor);
        return list(queryWrapper);
    }


    @Override
    public List<NodeDB> getNodeDBsByGroundCode(String groundCode) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(NodeDB::getGroundCode, groundCode);

        return this.list(queryWrapper);
    }

    @Override
    public List<NodeDB> getNodeDBListByRoadNo(NodeDB node) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getRoadNo, node.getRoadNo());
        return nodeMapper.selectList(queryWrapper);
    }

    @Override
    public List<Node> getNodeDBListByRoadNos(NodeDB node) {
        List<Node> nodes = new ArrayList<>();
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getRoadNo, node.getRoadNo()).eq(NodeDB::getCanToStop, true);
        List<NodeDB> nodeDBList = nodeMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(nodeDBList)){
            for (NodeDB nodeDB: nodeDBList) {
                nodes.add(new Node(nodeDB.getGridx(),nodeDB.getGridy(),nodeDB.getGridz()));
            }
        }
        return nodes;
    }


    @Override
    public List<NodeDB> getNodeDBListByRoadNo(String rodeNo, String direction) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getRoadNo, rodeNo);
        if (Direction.LEFT.getVal().equals(direction)) {
            queryWrapper.lambda().orderByDesc(NodeDB::getGridy);
        } else if (Direction.RIGHT.getVal().equals(direction)) {
            queryWrapper.lambda().orderByAsc(NodeDB::getGridy);
        } else if (Direction.UP.getVal().equals(direction)) {
            queryWrapper.lambda().orderByDesc(NodeDB::getGridx);
        } else if (Direction.DOWN.getVal().equals(direction)) {
            queryWrapper.lambda().orderByAsc(NodeDB::getGridx);
        }
        return nodeMapper.selectList(queryWrapper);
    }

    @Override
    public Set<Coord> getLinkedNode(Node node) {
        Set<Node> linkedNodeSet = getLinkedNodeSet(node);
        return new HashSet<>(linkedNodeSet);
    }

    @Override
    public Set<Node> getLinkedNodeSet(Node node) {
        NodeDB nodeDB = this.getNodeDB(node);
        Set<Node> linkedNodeSet = Sets.newHashSet();
        linkedNodeSet.add(node);
        if (checkPlusNodeDB(nodeDB)) {
            if (nodeDB.getCanToLeft() && PathProperties.LOCK_LEFT_COUNT != 0) {
                addPlusNode(node, linkedNodeSet, PlusType.LEFT, PathProperties.LOCK_LEFT_COUNT);
            }
            if (nodeDB.getCanToRight() && PathProperties.LOCK_RIGHT_COUNT != 0) {
                addPlusNode(node, linkedNodeSet, PlusType.RIGHT, PathProperties.LOCK_RIGHT_COUNT);
            }
            if (nodeDB.getCanToUp() && PathProperties.LOCK_UP_COUNT != 0) {
                addPlusNode(node, linkedNodeSet, PlusType.UP, PathProperties.LOCK_UP_COUNT);
            }
            if (nodeDB.getCanToDown() && PathProperties.LOCK_DOWN_COUNT != 0) {
                addPlusNode(node, linkedNodeSet, PlusType.DOWN, PathProperties.LOCK_DOWN_COUNT);
            }
        }
        return linkedNodeSet;
    }

    @Override
    public Set<Node> getLinkedPlusNodeSet(Node node) {
        NodeDB nodeDB = this.getNodeDB(node);
        Set<Node> linkedNodeSet = Sets.newHashSet();
        linkedNodeSet.add(node);
        if (checkPlusNodeDB(nodeDB)) {
            if (nodeDB.getCanToLeft() && PathProperties.LOCK_LEFT_COUNT != 0) {
                addPlusNode(node, linkedNodeSet, PlusType.LEFT, PathProperties.LOCK_LEFT_COUNT * 2);
            }
            if (nodeDB.getCanToRight() && PathProperties.LOCK_RIGHT_COUNT != 0) {
                addPlusNode(node, linkedNodeSet, PlusType.RIGHT, PathProperties.LOCK_RIGHT_COUNT * 2);
            }
            if (nodeDB.getCanToUp() && PathProperties.LOCK_UP_COUNT != 0) {
                addPlusNode(node, linkedNodeSet, PlusType.UP, PathProperties.LOCK_UP_COUNT * 2);
            }
            if (nodeDB.getCanToDown() && PathProperties.LOCK_DOWN_COUNT != 0) {
                addPlusNode(node, linkedNodeSet, PlusType.DOWN, PathProperties.LOCK_DOWN_COUNT * 2);
            }
        }
        return linkedNodeSet;
    }

    @Override
    public void addPlusNode(Node node, Set<Node> lockSet, Set<Node> avoidPlusNodeSet, PlusType plusType, int lockCount) {
        NodeDB plusNodeDB;
        for (int i = 0; i < lockCount; i++) {
            Node plusNode;
            if (PlusType.LEFT.equals(plusType)) {
                plusNode = new Node(node.getX(), node.getY() - i - 1, node.getZ());
                plusNodeDB = this.getNodeDB(plusNode);
                if (checkPlusNodeDB(plusNodeDB)) {
                    if (i < lockCount / 2) {
                        lockSet.add(plusNode);
                    }
                    avoidPlusNodeSet.add(plusNode);
                    if (!plusNodeDB.getCanToLeft()) {
                        break;
                    }
                } else {
                    break;
                }
            } else if (PlusType.RIGHT.equals(plusType)) {
                plusNode = new Node(node.getX(), node.getY() + i + 1, node.getZ());
                plusNodeDB = this.getNodeDB(plusNode);
                if (checkPlusNodeDB(plusNodeDB)) {
                    if (i < lockCount / 2) {
                        lockSet.add(plusNode);
                    }
                    avoidPlusNodeSet.add(plusNode);
                    if (!plusNodeDB.getCanToRight()) {
                        break;
                    }
                } else {
                    break;
                }
            } else if (PlusType.UP.equals(plusType)) {
                plusNode = new Node(node.getX() - i - 1, node.getY(), node.getZ());
                plusNodeDB = this.getNodeDB(plusNode);
                if (checkPlusNodeDB(plusNodeDB)) {
                    if (i < lockCount / 2) {
                        lockSet.add(plusNode);
                    }
                    avoidPlusNodeSet.add(plusNode);
                    if (!plusNodeDB.getCanToUp()) {
                        break;
                    }
                } else {
                    break;
                }
            } else if (PlusType.DOWN.equals(plusType)) {
                plusNode = new Node(node.getX() + i + 1, node.getY(), node.getZ());
                plusNodeDB = this.getNodeDB(plusNode);
                if (checkPlusNodeDB(plusNodeDB)) {
                    if (i < lockCount / 2) {
                        lockSet.add(plusNode);
                    }
                    avoidPlusNodeSet.add(plusNode);
                    if (!plusNodeDB.getCanToDown()) {
                        break;
                    }
                } else {
                    break;
                }
            }
        }
    }

    @Override
    public void addPlusNode(Node node, Set<Node> lockSet, PlusType plusType, int lockCount) {
        NodeDB plusNodeDB;
        for (int i = 0; i < lockCount; i++) {
            Node plusNode;
            if (PlusType.LEFT.equals(plusType)) {
                plusNode = new Node(node.getX(), node.getY() - i - 1, node.getZ());
                plusNodeDB = this.getNodeDB(plusNode);
                if (checkPlusNodeDB(plusNodeDB)) {
                    lockSet.add(plusNode);
                    if (!plusNodeDB.getCanToLeft()) {
                        break;
                    }
                } else {
                    break;
                }
            } else if (PlusType.RIGHT.equals(plusType)) {
                plusNode = new Node(node.getX(), node.getY() + i + 1, node.getZ());
                plusNodeDB = this.getNodeDB(plusNode);
                if (checkPlusNodeDB(plusNodeDB)) {
                    lockSet.add(plusNode);
                    if (!plusNodeDB.getCanToRight()) {
                        break;
                    }
                } else {
                    break;
                }
            } else if (PlusType.UP.equals(plusType)) {
                plusNode = new Node(node.getX() - i - 1, node.getY(), node.getZ());
                plusNodeDB = this.getNodeDB(plusNode);
                if (checkPlusNodeDB(plusNodeDB)) {
                    lockSet.add(plusNode);
                    if (!plusNodeDB.getCanToUp()) {
                        break;
                    }
                } else {
                    break;
                }
            } else if (PlusType.DOWN.equals(plusType)) {
                plusNode = new Node(node.getX() + i + 1, node.getY(), node.getZ());
                plusNodeDB = this.getNodeDB(plusNode);
                if (checkPlusNodeDB(plusNodeDB)) {
                    lockSet.add(plusNode);
                    if (!plusNodeDB.getCanToDown()) {
                        break;
                    }
                } else {
                    break;
                }
            }
        }
    }

    /**
     * 校验该节点是否需要添加相关联节点
     *
     * @param plusNodeDB
     * @return
     */
    @Override
    public boolean checkPlusNodeDB(NodeDB plusNodeDB) {
        if (plusNodeDB != null) {
            if (NodeType.P.equals(plusNodeDB.getGridType()) || NodeType.C.equals(plusNodeDB.getGridType())) {
                return true;
            } else if (checkRemoveRoad(plusNodeDB)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 通道看配置，其他充电桩或接驳点或自动门，需要加去掉，不需要加就保留
     *
     * @param plusNodeDB
     * @return
     */
    @Override
    public boolean checkRemoveRoad(NodeDB plusNodeDB) {
        if (plusNodeDB != null && !PathProperties.REMOVE_ROAD && (NodeType.R.equals(plusNodeDB.getGridType())
                || NodeType.C.equals(plusNodeDB.getGridType())
                || NodeType.CONNECTION.equals(plusNodeDB.getGridType())
                || NodeType.AD.equals(plusNodeDB.getGridType()))) {
            return true;
        }
        return false;
    }

    /**
     * 检测小车位置是否可停车
     *
     * @param rgvNode
     * @return
     */
    public boolean checkRgvNodeCanStop(String rgvNode) {
        Node node = Node.parseGrid(rgvNode);
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();

        queryWrapper.lambda().eq(NodeDB::getGridx, node.getX()).eq(NodeDB::getGridy, node.getY()).eq(NodeDB::getGridz,
                node.getZ());
        NodeDB nodeDB = this.getOne(queryWrapper);
        if (nodeDB != null) {
            if (nodeDB.getCanToStop()) {
                return true;
            } else {
                return false;
            }
        }

        return true;
    }

    /**
     * 校验提升机左右是否有货
     *
     * @param coord
     * @return boolean
     */
    @Override
    public boolean checkLifterLeftOrRightGridStatus(Coord coord, NodeStatus nodeStatus) {
        LifterOper lifterOper = (LifterOper) Plc.deviceLifterCoordMap.get(new Coord(coord.getX(), coord.getY(), 1));
        if (lifterOper != null && (Location.LEFT.equals(lifterOper.getLocation()) || Location.RIGHT.equals(lifterOper.getLocation()))) {
            NodeDB nodeDB = this.getNodeDB(coord);
            if (nodeDB != null && nodeStatus.name().equals(nodeDB.getGridStatus())) {
                return true;
            } else {
                return false;
            }
        }
        return true;
    }

    /**
     * X坐标的最大值
     *
     * @return
     */
    @Override
    public Integer getMaxX() {
        Integer maxX = baseMapper.getMaxX();
        return maxX == null ? 0 : maxX;
    }

    /**
     * Y坐标的最大值
     *
     * @return
     */
    @Override
    public Integer getMaxY() {
        Integer maxY = baseMapper.getMaxY();
        return maxY == null ? 0 : maxY;
    }

    /**
     * Z坐标的最大值
     *
     * @return
     */
    @Override
    public Integer getMaxZ() {
        Integer maxZ = baseMapper.getMaxZ();
        return maxZ == null ? 0 : maxZ;
    }

    @Override
    public List<NodeDB> selectNodeList(NodeDB nodeDB) {
        LambdaQueryWrapper<NodeDB> lambdaQueryWrapper = new LambdaQueryWrapper();
        if (StringUtils.isNotEmpty(nodeDB.getGridId())) {
            lambdaQueryWrapper.like(NodeDB::getGridId, nodeDB.getGridId());
        }
        if (StringUtils.isNotEmpty(nodeDB.getGroundCode())) {
            lambdaQueryWrapper.like(NodeDB::getGroundCode, nodeDB.getGroundCode());
        }
        if (StringUtils.isNotNull(nodeDB.getGridType())) {
            lambdaQueryWrapper.like(NodeDB::getGridType, nodeDB.getGridType());
        }
        if (StringUtils.isNotNull(nodeDB.getPalletCode())) {
            lambdaQueryWrapper.like(NodeDB::getPalletCode, nodeDB.getPalletCode());
        }
        if (StringUtils.isNotNull(nodeDB.getGridx())) {
            lambdaQueryWrapper.eq(NodeDB::getGridx, nodeDB.getGridx());
        }
        if (StringUtils.isNotNull(nodeDB.getGridy())) {
            lambdaQueryWrapper.eq(NodeDB::getGridy, nodeDB.getGridy());
        }
        return nodeMapper.selectList(lambdaQueryWrapper);
    }

    /*检验传入节点是不是单开口巷道*/

    /**
     * 如果巷道号为空，且为货位，认为是单开口巷道
     * 如果巷道号不为空，且巷道类型为单开口，且为货位或接驳点或通道
     *
     * @param nodeDB
     * @return
     */
    @Override
    public boolean isSingleRoad(NodeDB nodeDB) {
        return (NodeType.P.equals(nodeDB.getGridType()) && StringUtils.isBlank(nodeDB.getRoadNo()))
                || (StringUtils.isNotBlank(nodeDB.getRoadNo()) && RoadType.S.getValue().equals(nodeDB.getRoadType()) && (NodeType.P.equals(nodeDB.getGridType()) || NodeType.CONNECTION.equals(nodeDB.getGridType()) || NodeType.R.equals(nodeDB.getGridType())));
    }

    /*检验传入节点是不是双开口巷道*/
    @Override
    public boolean isDoubleRoad(NodeDB nodeDB) {
        return RoadType.D.getValue().equals(nodeDB.getRoadType()) && NodeType.P.equals(nodeDB.getGridType());
    }

    /* 得到巷道内全部的节点集合*/
    @Override
    public List<Node> getNodeListFromRoad(int x, int y, int z) {
        List<Node> roadNodeList = new ArrayList<>();
        NodeDB node = this.getNodeDB(new Node(x, y, z));
        if (node != null) {
            List<NodeDB> roadRuleDBS = this.getNodeDBListByRoadNo(node);
            if (roadRuleDBS != null) {
                for (NodeDB nodeDB : roadRuleDBS) {
                    roadNodeList.add(new Node(nodeDB.getGridx(), nodeDB.getGridy(), nodeDB.getGridz()));
                }
            }
        }
        return roadNodeList;
    }

    /* 得到巷道内全部的节点集合*/
    @Override
    public List<Node> getNodeListFromRoad(String roadNo, String direction) {
        List<Node> roadNodeList = new ArrayList<>();
        if (roadNo != null) {
            List<NodeDB> roadRuleDBS = this.getNodeDBListByRoadNo(roadNo, direction);
            if (roadRuleDBS != null) {
                for (NodeDB nodeDB : roadRuleDBS) {
                    roadNodeList.add(new Node(nodeDB.getGridx(), nodeDB.getGridy(), nodeDB.getGridz()));
                }
            }
        }
        return roadNodeList;
    }

    /**
     * 通过类型查询某一层的数据
     *
     * @param code
     * @param layer
     * @return
     */
    @Override
    public List<NodeDB> getNodeDBByFloorAndType(String code, Integer layer) {
        LambdaQueryWrapper<NodeDB> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(NodeDB::getGridType, code).eq(NodeDB::getGridz, layer);
        return list(lambdaQueryWrapper);
    }

    /* 得到巷道内全部的节点集合*/
    @Override
    public List<Node> getNodeDBListByRoadNoForOrder(Node node) {
        List<Node> roadNodeList = new ArrayList<>();
        NodeDB nodedb = this.getNodeDB(node);
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getRoadNo, nodedb.getRoadNo())
                .eq(NodeDB::getGridStatus, NodeStatus.Y.toString());
        List<NodeDB> nodeDBS = nodeMapper.selectList(queryWrapper);
        if (nodeDBS != null) {
            for (NodeDB nodeDB : nodeDBS) {
                roadNodeList.add(new Node(nodeDB.getGridx(), nodeDB.getGridy(), nodeDB.getGridz()));
            }
        }
        return roadNodeList;
    }

    @Override
    public void updateNodeDBByTask(TaskDB taskDB) {
        if (TaskType.IN.equals(taskDB.getTaskType())) {
            NodeDB nodedb = getNodeDB(taskDB.selectEndNode());
            if (!NodeType.CONNECTION.equals(nodedb.getGridType())) {
                updateNodeDBStatus(taskDB.selectEndNode(), NodeStatus.Y.toString());
            }
        } else if (TaskType.OUT.equals(taskDB.getTaskType())) {
            NodeDB nodedb = getNodeDB(taskDB.selectStartNode());
            if (!NodeType.CONNECTION.equals(nodedb.getGridType())) {
                updateNodeDBStatus(taskDB.selectStartNode(), NodeStatus.N.toString());
            }
        } else if (TaskType.TRANSFER.equals(taskDB.getTaskType())) {
            NodeDB start = getNodeDB(taskDB.selectStartNode());
            if (!NodeType.CONNECTION.equals(start.getGridType())) {
                updateNodeDBStatus(taskDB.selectStartNode(), NodeStatus.N.toString());
            }
            NodeDB end = getNodeDB(taskDB.selectEndNode());
            if (!NodeType.CONNECTION.equals(end.getGridType())) {
                updateNodeDBStatus(taskDB.selectEndNode(), NodeStatus.Y.toString());
            }
        }
    }

    @Override
    public int updateNodeDBInfo(NodeDB nodedb) {
        LambdaUpdateWrapper<NodeDB> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(NodeDB::getGridx, nodedb.getGridx())
                .set(NodeDB::getGridy, nodedb.getGridy())
                .set(NodeDB::getGridz, nodedb.getGridz())
                .set(NodeDB::getGridType, nodedb.getGridType())
                .set(NodeDB::getGridStatus, nodedb.getGridStatus())
                .set(NodeDB::getCanToStop, nodedb.getCanToStop())
                .set(NodeDB::getCanToDown, nodedb.getCanToDown())
                .set(NodeDB::getCanToLeft, nodedb.getCanToLeft())
                .set(NodeDB::getCanToRight, nodedb.getCanToRight())
                .set(NodeDB::getCanToUp, nodedb.getCanToUp())
                .set(NodeDB::getRoadNo, nodedb.getRoadNo())
                .set(NodeDB::getGroundCode, nodedb.getGroundCode())
                .set(NodeDB::getGridId, nodedb.getGridId())
                .set(NodeDB::getIsDoor, nodedb.getIsDoor())
                .set(NodeDB::getOffsetx, nodedb.getOffsetx())
                .set(NodeDB::getOffsety, nodedb.getOffsety())
                .set(NodeDB::getLimitRgvCount, nodedb.getLimitRgvCount())
                .set(NodeDB::getPalletCode, nodedb.getPalletCode())
                .set(NodeDB::getZoneId, nodedb.getZoneId())
                .set(NodeDB::getSingleNo, nodedb.getSingleNo())
                .set(NodeDB::getRoadType, nodedb.getRoadType())
                .set(NodeDB::getSingleOrder, nodedb.getSingleOrder())
                .set(NodeDB::getRunUpForGroundCodeStatus,nodedb.getRunUpForGroundCodeStatus())
                .set(NodeDB::getRunDownForGroundCodeStatus,nodedb.getRunDownForGroundCodeStatus())
                .set(NodeDB::getRunLeftForGroundCodeStatus,nodedb.getRunLeftForGroundCodeStatus())
                .set(NodeDB::getRunRightForGroundCodeStatus,nodedb.getRunRightForGroundCodeStatus())
                .set(NodeDB::getUpCalibrationTime,nodedb.getUpCalibrationTime())
                .set(NodeDB::getDownCalibrationTime,nodedb.getDownCalibrationTime())
                .set(NodeDB::getLeftCalibrationTime,nodedb.getLeftCalibrationTime())
                .set(NodeDB::getRightCalibrationTime,nodedb.getRightCalibrationTime())
                .set(NodeDB::getDirection, nodedb.getDirection())
                .set(NodeDB::getTag, nodedb.getTag())
                .set(NodeDB::getForkHeight, nodedb.getForkHeight())
                .set(NodeDB::getRgvPoint, nodedb.getRgvPoint())
                .set(NodeDB::getForbidRgvs, nodedb.getForbidRgvs());
        updateWrapper.eq(NodeDB::getNodeId, nodedb.getNodeId());
        int result = nodeMapper.update(null, updateWrapper);
        NodeInfo nodeInfo = new NodeInfo();
        BeanUtils.copyProperties(nodedb, nodeInfo);
        NodeInfoPush.push(nodeInfo);
        return result;
    }

    public Node getNode(Node node) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(NodeDB::getGridx, node.getX()).
                eq(NodeDB::getGridy, node.getY()).eq(NodeDB::getGridz, node.getZ());
        NodeDB nodeDB1 = nodeMapper.selectOne(queryWrapper);
        Node returnNode = null;
        if (nodeDB1 != null) {
            returnNode = new Node(new Coord(nodeDB1.getGridx(), nodeDB1.getGridy(), nodeDB1.getGridz()),
                    nodeDB1.getGridType(), NodeStatus.parse(nodeDB1.getGridStatus()), nodeDB1.getCanToUp(), nodeDB1.getCanToDown(),
                    nodeDB1.getCanToLeft(), nodeDB1.getCanToRight(), nodeDB1.getCanToStop(),
                    nodeDB1.getIsDoor(), nodeDB1.getRgvPoint(),nodeDB1.getTag(), nodeDB1.getForkHeight(),
                    nodeDB1.getForbidRgvs() );
            node.setOffsetX(nodeDB1.getOffsetx());
            node.setOffsetY(nodeDB1.getOffsety());
        }
        return returnNode;
    }

    public boolean isCanPut(Coord coord) {
        DeviceBase deviceBase = Plc.deviceStationCoordMap.get(coord);
        if (deviceBase != null) {
            if (baseCheck(deviceBase)) return false;
            if (deviceBase.getPlcDeviceType() == PlcDeviceType.CHAIN_STATION) {
                StationOper stationOper = (StationOper) deviceBase;
                return stationOper.isCanPut() && !stationOper.isRunIng();
            } else {
                DeviceInterface deviceInterface = (DeviceInterface) deviceBase;
                return !deviceInterface.hasCargo() && !deviceInterface.isRunning()
                        && !deviceInterface.isError();
            }
        } else {
            LifterOper lifterOper = PlcUnit.getLifterOper(coord);
            if (lifterOper == null) {
                LoggerUtil.warn(coord.toString() + "未获取到对应工位或者提升机信息", this.getClass());
                return NodeStatus.N.equals(NodeStatus.parse(getNodeDB(coord).getGridStatus()));
                //throw new PlcExecuteException("此坐标" + coord.toString() + "无对应设备！请检查配置");
            } else if (lifterOper instanceof ForkLifterOper) {
                if (baseCheck(lifterOper)) return false;
                if (lifterOper.getLocation() == Location.INSIDE) {
                    //货叉提升机 提升机内货物状态 查询光电信号
                    return !lifterOper.isHaveCargo();
                }
                //货叉提升机货物状态，查询节点货物状态
                return NodeStatus.N.equals(NodeStatus.parse(getNodeDB(coord).getGridStatus()));
            } else {
                if (baseCheck(lifterOper)) return false;
                return !lifterOper.isPalletExisted(coord.getZ(), lifterOper.getLocation()) && !lifterOper.isFacilityExisted(coord.getZ(), lifterOper.getLocation()) && !lifterOper.disconnect();
            }
        }
    }

    public boolean isCanGet(Coord coord) {
        DeviceBase deviceBase = Plc.deviceStationCoordMap.get(coord);
        if (deviceBase != null) {
            if (baseCheck(deviceBase)) return false;
            if (deviceBase.getPlcDeviceType() == PlcDeviceType.CHAIN_STATION) {
                StationOper stationOper = (StationOper) deviceBase;
                return stationOper.isCanGet() && !stationOper.isRunIng();
            } else {
                DeviceInterface deviceInterface = (DeviceInterface) deviceBase;
                return deviceInterface.hasCargo() && !deviceInterface.isRunning()
                        && !deviceInterface.isError();
            }
        } else {
            LifterOper lifterOper = PlcUnit.getLifterOper(coord);
            if (lifterOper == null) {
                LoggerUtil.warn(coord.toString() + "未获取到对应工位或者提升机信息", this.getClass());
                return NodeStatus.Y.equals(NodeStatus.parse(getNodeDB(coord).getGridStatus()));
                //throw new PlcExecuteException("此坐标" + coord.toString() + "无对应设备！请检查配置");
            } else if (lifterOper instanceof ForkLifterOper) {
                if (baseCheck(lifterOper)) return false;
                if (lifterOper.getLocation() == Location.INSIDE) {
                    //货叉提升机 提升机内货物状态 查询光电信号
                    return lifterOper.isHaveCargo();
                }
                //货叉提升机货物状态，查询节点货物状态
                return NodeStatus.Y.equals(NodeStatus.parse(getNodeDB(coord).getGridStatus()));
            } else {
                if (baseCheck(lifterOper)) return false;
                return lifterOper.isPalletExisted(coord.getZ(), lifterOper.getLocation()) && !lifterOper.isFacilityExisted(coord.getZ(), lifterOper.getLocation()) && !lifterOper.disconnect();
            }
        }
    }

    public boolean isHaveCargo(Coord coord) throws PlcExecuteException {
        DeviceBase deviceBase = Plc.deviceStationCoordMap.get(coord);
        if (deviceBase != null) {
            if (baseCheck(deviceBase)) return false;
            if (deviceBase.getPlcDeviceType() == PlcDeviceType.CHAIN_STATION) {
                return ((StationOper) deviceBase).isPalletExist();
            } else {
                return ((DeviceInterface) deviceBase).hasCargo();
            }
        } else {
            LifterOper lifterOper = PlcUnit.getLifterOper(coord);
            if (lifterOper == null) {
                LoggerUtil.warn(coord.toString() + "未获取到对应工位或者提升机信息", this.getClass());
                return NodeStatus.Y.equals(NodeStatus.parse(getNodeDB(coord).getGridStatus()));
                //throw new PlcExecuteException("此坐标" + coord.toString() + "无对应设备！请检查配置");
            } else if (lifterOper instanceof ForkLifterOper) {
                if (baseCheck(lifterOper)) return false;
                if (lifterOper.getLocation() == Location.INSIDE) {
                    //货叉提升机 提升机内货物状态 查询光电信号
                    return lifterOper.isHaveCargo();
                }
                //货叉提升机货物状态，查询节点货物状态
                return NodeStatus.Y.equals(NodeStatus.parse(getNodeDB(coord).getGridStatus()));
            } else {
                if (baseCheck(lifterOper)) return false;
                return lifterOper.isPalletExisted(coord.getZ(), lifterOper.getLocation());
            }
        }
    }

    private boolean baseCheck(DeviceBase deviceBase) {
        if (deviceBase.getPlc().getPlcStatus() == Network.OFF_LINE) {
            LoggerUtil.info(deviceBase.getDeviceName() + "离线，不能取货", deviceBase.getClass());
            return true;
        }

        if (deviceBase.getPlcDeviceType() == PlcDeviceType.FORK_LIFTER) {
            ForkLifterOper forkLifterOper = (ForkLifterOper) deviceBase;
            if (forkLifterOper.getPlc().getPlcCache().isTimeOut()) {
                LoggerUtil.info(deviceBase.getDeviceName() + "缓存数据已超时，不能取货", deviceBase.getClass());
                return true;
            }
        } else {
            if (deviceBase.getPlc().getPlcCache().isTimeOut()) {
                LoggerUtil.info(deviceBase.getDeviceName() + "plc缓存数据已超时，不能取货", deviceBase.getClass());
                return true;
            }
        }
        return false;
    }

    @Override
    public int selectCountByStatus(NodeStatus nodeStatus) {
        LambdaQueryWrapper<NodeDB> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(NodeDB::getGridStatus, nodeStatus.toString());
        lambdaQueryWrapper.eq(NodeDB::getGridType, NodeType.P);
        return this.count(lambdaQueryWrapper);
    }

    @Override
    public int selectCountByType(NodeType nodeType) {
        LambdaQueryWrapper<NodeDB> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(NodeDB::getGridType, nodeType);
        return this.count(lambdaQueryWrapper);
    }

    public NodeAndPlanBO getMostAccessibleNodeAndPlan(String startNodeStr, String endNodeStr, TaskType taskType) {
        RouteFactoryImp routeFactoryStartFloor = new RouteFactoryImp(getNodeList(), LockNodeManager.getLockedNodes(),
                null, 0);
        NodeChooseImpl nodeChooseImpl = new NodeChooseImpl(routeFactoryStartFloor, null);
        NodeAndPlanInfoBO nodeAndPlanGridIdBO = null;
        List<ProgrammeDB> programmeDBS = null;

        NodeAndPlanBO nodeAndPlanBO = null;
        if (taskType.equals(TaskType.IN)) {
            Node endNode = Node.parseGrid(endNodeStr);
            programmeDBS = SystemInit.getProgrammeByPortAndType(startNodeStr, PortModel.IN_PORT);
            nodeAndPlanGridIdBO = nodeChooseImpl.getMostAccessibleNodeAndPlanGridId(endNode,
                    portCoordsToNodeAndPlanGridId(programmeDBS, endNode));
        } else if (taskType.equals(TaskType.OUT)) {
            Node startNode = Node.parseGrid(startNodeStr);
            programmeDBS = SystemInit.getProgrammeByPortAndType(endNodeStr, PortModel.OUT_PORT);
            nodeAndPlanGridIdBO = nodeChooseImpl.getMostAccessibleNodeAndPlanGridId(startNode,
                    portCoordsToNodeAndPlanGridId(programmeDBS, startNode));
        }

        if (nodeAndPlanGridIdBO != null) {
            nodeAndPlanBO = new NodeAndPlanBO();
            NodeDB nodedb = getNodeDB(nodeAndPlanGridIdBO.getNode());
            nodeAndPlanBO.setNodedb(nodedb);
            String planGridId = nodeAndPlanGridIdBO.getPlanGridId();
            nodeAndPlanBO.setWcsPortdb(programmeDBS.stream().filter(x -> x.getProgrammeName().equals(planGridId)).findFirst().orElse(null));
        }

        return nodeAndPlanBO;
    }

    /**
     * 根据接驳点配置返回接驳任务的接驳点
     *
     * @param startNodeStr
     * @param endNodeStr
     * @param taskType
     * @param connectConfDBS
     * @return
     */
    public ConnectNodeBO getMostConnectNodeAndPlan(String startNodeStr, String endNodeStr, TaskType taskType, List<ConnectConfDB> connectConfDBS) {
        if (CollectionUtils.isEmpty(connectConfDBS)) {
            return null;
        }
        List<ConnectPlanBO> connectPlanBOList;
        ConnectPlanBO connectPlanBO = null;
        ConnectNodeBO connectNodeBO = null;

        boolean isStartAndPre;
        Node targetNode;

        List<ProgrammeDB> programmeDBS;
        if (taskType.equals(TaskType.IN)) {
            Node endNode = Node.parseGrid(endNodeStr);
            programmeDBS = SystemInit.getProgrammeByPortAndType(startNodeStr, PortModel.IN_PORT);

            connectPlanBOList = connectToInOrOutPlanBO(programmeDBS, getAvailableConnectConfDB(connectConfDBS), endNode, taskType);
            targetNode = endNode;
            isStartAndPre = true;
        } else if (taskType.equals(TaskType.OUT)) {
            Node startNode = Node.parseGrid(startNodeStr);
            programmeDBS = SystemInit.getProgrammeByPortAndType(endNodeStr, PortModel.OUT_PORT);

            connectPlanBOList = connectToInOrOutPlanBO(programmeDBS, getAvailableConnectConfDB(connectConfDBS), startNode, taskType);
            targetNode = startNode;
            isStartAndPre = false;
        } else {
            Node startNode = Node.parseGrid(startNodeStr);
            Node endNode = Node.parseGrid(endNodeStr);

            connectPlanBOList = connectToTransferPlanBO(getAvailableConnectConfDB(connectConfDBS), startNode, endNode);
            targetNode = startNode;
            isStartAndPre = false;
        }

        if (CollectionUtils.isNotEmpty(connectPlanBOList)) {
            connectPlanBO = getMostAccessibleConnectBO(connectPlanBOList, targetNode, isStartAndPre);
        }

        if (connectPlanBO != null) {
            connectNodeBO = new ConnectNodeBO();
            NodeDB nodedb = getNodeDB(connectPlanBO.getTargetNode());
            connectNodeBO.setNodedb(nodedb);
            connectNodeBO.setPreConnect(connectPlanBO.getPreConnect());
            connectNodeBO.setSufNodeConnect(connectPlanBO.getSufNodeConnect());
            connectNodeBO.setConnectPortCode(connectPlanBO.getPortCode());
        }

        return connectNodeBO;
    }

    /**
     * 获取去可到达指定层的方案与坐标
     *
     * @param programmeDBS
     * @param targetNode
     * @return
     */
    public List<NodeAndPlanInfoBO> portCoordsToNodeAndPlanGridId(List<ProgrammeDB> programmeDBS, Node targetNode) {
        int targetLayer = targetNode.getZ();
        List<NodeAndPlanInfoBO> nodeAndPlanGridIdBOS = new ArrayList<>();
        for (ProgrammeDB programmeDB : programmeDBS) {
            for (String coord : programmeDB.TransConnectCoord()) {
                Node node = Node.parseGrid(coord);
                if (node.getZ() == targetLayer) {
                    NodeAndPlanInfoBO nodeAndPlanGridIdBO = new NodeAndPlanInfoBO();
                    nodeAndPlanGridIdBO.setNode(node);
                    nodeAndPlanGridIdBO.setPlanCost(programmeDB.getCost() == null ? 0 : programmeDB.getCost().intValue());
                    nodeAndPlanGridIdBO.setPlanGridId(programmeDB.getProgrammeName());
                    nodeAndPlanGridIdBOS.add(nodeAndPlanGridIdBO);
                }
            }
        }
        return nodeAndPlanGridIdBOS;
    }

    /**
     * 获取出库或入库接驳配置
     * 获取去可到达指定层的方案与坐标
     *
     * @param programmeDBS
     * @param confDBList
     * @param targetNode
     * @return
     */
    public List<ConnectPlanBO> connectToInOrOutPlanBO(List<ProgrammeDB> programmeDBS, List<ConnectConfDB> confDBList, Node targetNode, TaskType taskType) {
        int layer = targetNode.getZ();
        List<NodeAndPlanInfoBO> nodeAndPlanGridIdBOS = new ArrayList<>();
        List<ConnectNodeBO> connectNodeBOList = new ArrayList<>();

        List<ConnectPlanBO> connectPlanBOList = new ArrayList<>();
        // 查找对应库口的所有出/入库方案
        for (ProgrammeDB programmeDB : programmeDBS) {
            for (String coord : programmeDB.TransConnectCoord()) {
                Node node = Node.parseGrid(coord);
                NodeAndPlanInfoBO nodeAndPlanGridIdBO = new NodeAndPlanInfoBO();
                nodeAndPlanGridIdBO.setNode(node);
                nodeAndPlanGridIdBO.setPlanCost(programmeDB.getCost() == null ? 0 : programmeDB.getCost().intValue());
                nodeAndPlanGridIdBO.setPlanGridId(programmeDB.getProgrammeName());
                nodeAndPlanGridIdBO.setLayer(programmeDB.getProgrammeFloor());
                nodeAndPlanGridIdBOS.add(nodeAndPlanGridIdBO);
            }
        }

        // 自动接驳点组合
        for (ConnectConfDB confDB : confDBList) {
            for (String preNodeStr : confDB.transPreConnect()) {
                for (String sufNodeStr : confDB.transSufConnect()) {
                    ConnectNodeBO connectNodeBO = new ConnectNodeBO();
                    connectNodeBO.setPreConnect(Node.parseGrid(preNodeStr));
                    connectNodeBO.setSufNodeConnect(Node.parseGrid(sufNodeStr));
                    connectNodeBO.setConnectPortCode(confDB.getPortCode());
                    connectNodeBOList.add(connectNodeBO);
                }
            }
        }
        ConnectPlanBO connectPlanBO;
        for (NodeAndPlanInfoBO gridIdBOS : nodeAndPlanGridIdBOS) {
            if (gridIdBOS.getLayer() == layer) {
                connectPlanBO = new ConnectPlanBO();
                connectPlanBO.setTargetNode(gridIdBOS.getNode());
                connectPlanBOList.add(connectPlanBO);
            } else {
                boolean checkLayer;
                for (ConnectNodeBO connectNodeBO : connectNodeBOList) {
                    if (TaskType.IN.equals(taskType)) {
                        checkLayer = connectNodeBO.getPreConnect().getZ() == gridIdBOS.getNode().getZ()
                                && connectNodeBO.getSufNodeConnect().getZ() == layer;
                    } else {
                        checkLayer = connectNodeBO.getSufNodeConnect().getZ() == gridIdBOS.getNode().getZ()
                                && connectNodeBO.getPreConnect().getZ() == layer;
                    }
                    if (checkLayer) {
                        connectPlanBO = new ConnectPlanBO();
                        connectPlanBO.setTargetNode(gridIdBOS.getNode());
                        connectPlanBO.setPreConnect(connectNodeBO.getPreConnect());
                        connectPlanBO.setSufNodeConnect(connectNodeBO.getSufNodeConnect());
                        connectPlanBO.setPortCode(connectNodeBO.getConnectPortCode());
                        connectPlanBOList.add(connectPlanBO);
                    }
                }
            }
        }

        return connectPlanBOList;
    }

    /**
     * 移库接驳点匹配
     * 获取去可到达指定层的方案与坐标
     *
     * @param confDBList
     * @param startNode
     * @param endNode
     * @return
     */
    public List<ConnectPlanBO> connectToTransferPlanBO(List<ConnectConfDB> confDBList, Node startNode, Node endNode) {
        List<ConnectPlanBO> connectPlanBOList = new ArrayList<>();

        for (ConnectConfDB confDB : confDBList) {
            for (String preNodeStr : confDB.transPreConnect()) {
                Node start = Node.parseGrid(preNodeStr);
                if (start.getZ() == startNode.getZ()) {
                    for (String sufNodeStr : confDB.transSufConnect()) {
                        Node end = Node.parseGrid(sufNodeStr);
                        if (end.getZ() == endNode.getZ()) {
                            ConnectPlanBO connectPlanBO = new ConnectPlanBO();
                            connectPlanBO.setTargetNode(endNode);
                            connectPlanBO.setPreConnect(Node.parseGrid(preNodeStr));
                            connectPlanBO.setSufNodeConnect(Node.parseGrid(sufNodeStr));
                            connectPlanBO.setPortCode(confDB.getPortCode());
                            connectPlanBOList.add(connectPlanBO);
                        }
                    }
                }
            }
        }

        return connectPlanBOList;
    }

    /**
     * 获取当前巷道无货的最深位置
     *
     * @param start
     * @param end
     * @param taskType
     * @return
     */
    @Override
    public Node getEndIsDeepNode(Node start, Node end, TaskType taskType) {
        Node node = null;

        NodeDB nodeDB = this.getNodeDB(end);
        String roadNo = nodeDB.getRoadNo();
        //如果没有划分单巷道则不用改变终点
        if (org.apache.commons.lang3.StringUtils.isBlank(roadNo)) {
            return end;
        }
        String singleOrder = nodeDB.getSingleOrder();
        String singleOrderReverse = org.apache.commons.lang3.StringUtils.EMPTY;
        List<NodeDB> nodeDBNList = this.getNodeDBListBySingle(roadNo, singleOrder, NodeStatus.N.name());
        if (Direction.UP.getCode().equals(singleOrder)) {
            singleOrderReverse = Direction.DOWN.getCode();
        } else if (Direction.DOWN.getCode().equals(singleOrder)) {
            singleOrderReverse = Direction.UP.getCode();
        } else if (Direction.LEFT.getCode().equals(singleOrder)) {
            singleOrderReverse = Direction.RIGHT.getCode();
        } else if (Direction.RIGHT.getCode().equals(singleOrder)) {
            singleOrderReverse = Direction.LEFT.getCode();
        }
        List<NodeDB> nodeDBYList = this.getNodeDBListBySingle(roadNo, singleOrderReverse, NodeStatus.Y.name());
        // 移库任务起点在同一深位的处理
        if (CollectionUtils.isNotEmpty(nodeDBYList) && TaskType.TRANSFER.equals(taskType)) {
            NodeDB nodeDBStart = this.getNodeDB(start);
            String roadNoStart = nodeDBStart.getRoadNo();

            Iterator iteratorNodeY = nodeDBYList.iterator();
            while (iteratorNodeY.hasNext()) {
                NodeDB nodeDBY = (NodeDB) iteratorNodeY.next();
                if (start.getX() == nodeDBY.getGridx()
                        && start.getY() == nodeDBY.getGridy()
                        && start.getZ() == nodeDBY.getGridz()) {
                    iteratorNodeY.remove();
                } else if (roadNo.equals(roadNoStart)) {
                    if (Direction.RIGHT.getCode().equals(singleOrder) && start.getY() > nodeDBY.getGridy()) {
                        iteratorNodeY.remove();
                    }
                    if (Direction.LEFT.getCode().equals(singleOrder) && start.getY() < nodeDBY.getGridy()) {
                        iteratorNodeY.remove();
                    }
                    if (Direction.DOWN.getCode().equals(singleOrder) && start.getX() > nodeDBY.getGridx()) {
                        iteratorNodeY.remove();
                    }
                    if (Direction.UP.getCode().equals(singleOrder) && start.getX() < nodeDBY.getGridx()) {
                        iteratorNodeY.remove();
                    }
                }
            }
        }
        if (CollectionUtils.isNotEmpty(nodeDBNList)) {
            if (CollectionUtils.isEmpty(nodeDBYList)) {
                node = getHasTaskNode(nodeDBNList);
            } else {
                //找出最深位，有可能误操作，导致隔货位有货或无货
                NodeDB nodeDBY = nodeDBYList.get(nodeDBYList.size() - 1);
                Iterator iteratorSec = nodeDBNList.iterator();
                while (iteratorSec.hasNext()) {
                    NodeDB noDB = (NodeDB) iteratorSec.next();
                    if (Direction.UP.getCode().equals(singleOrder)) {
                        if (noDB.getGridx() < nodeDBY.getGridx()) {
                            iteratorSec.remove();
                        }
                    } else if (Direction.DOWN.getCode().equals(singleOrder)) {
                        if (noDB.getGridx() > nodeDBY.getGridx()) {
                            iteratorSec.remove();
                        }
                    } else if (Direction.LEFT.getCode().equals(singleOrder)) {
                        if (noDB.getGridy() < nodeDBY.getGridy()) {
                            iteratorSec.remove();
                        }
                    } else if (Direction.RIGHT.getCode().equals(singleOrder)) {
                        if (noDB.getGridy() > nodeDBY.getGridy()) {
                            iteratorSec.remove();
                        }
                    }
                }
                if (CollectionUtils.isNotEmpty(nodeDBNList)) {
                    node = getHasTaskNode(nodeDBNList);
                }
            }
        }
        return node;
    }

    private Node getHasTaskNode(List<NodeDB> nodeDBNList) {
        Node node = null;
        Collections.reverse(nodeDBNList);
        Iterator<NodeDB> nodeDBNIterator = nodeDBNList.iterator();
        while (nodeDBNIterator.hasNext()) {
            NodeDB nDB = nodeDBNIterator.next();
            List<TaskDB> taskDBList = taskService.findUnFinishInOrTransferByEndNode(new Node(nDB.getGridx(), nDB.getGridy(), nDB.getGridz()));
            if (CollectionUtils.isNotEmpty(taskDBList)) {
                node = buildNode(nDB);
                break;
            }
        }
        return node;
    }


    public List<NodeDB> getNodeListLift(PlcDeviceDB plcDeviceDB) {
        Node lifterNode = Node.parseGrid(plcDeviceDB.getPlcLockLocation());
        LambdaQueryWrapper<NodeDB> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(NodeDB::getGridx, lifterNode.getX()).eq(NodeDB::getGridy, lifterNode.getY());
        return this.list(lambdaQueryWrapper);
    }

    @Override
    public Node getNodeByRgvPoint(String rgv60Point) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(NodeDB::getRgvPoint, rgv60Point);
        List<NodeDB> nodeDBs = this.list(queryWrapper);
        if (nodeDBs.size() > 0) {
            NodeDB nodeDB = nodeDBs.get(0);
            Node node = new Node(nodeDB.getGridx(), nodeDB.getGridy(), nodeDB.getGridz());
            node.setNodeType(nodeDB.getGridType());
            node.setRgv60Point(rgv60Point);
            return node;
        }

        return null;
    }

    /**
     * 计算最短接驳点
     *
     * @param connectPlanBOS
     * @param targetNode
     * @param isStartAndPre
     * @return
     */
    public ConnectPlanBO getMostAccessibleConnectBO(List<ConnectPlanBO> connectPlanBOS, Node targetNode, boolean isStartAndPre) {
        long minPathCost = -1;
        int bestNodeIndex = -1;
        for (int i = 0; i < connectPlanBOS.size(); i++) {
            ConnectPlanBO connectPlanBO = connectPlanBOS.get(i);
            long firstPath;
            long lastPath = 0L;
            int taskCount = 0;
            try {
                if (connectPlanBO.getPreConnect() == null || connectPlanBO.getSufNodeConnect() == null) { // 不需要接驳
                    firstPath = Math.abs(connectPlanBO.getTargetNode().getX() - targetNode.getX()) + Math.abs(connectPlanBO.getTargetNode().getY() - targetNode.getY());
                } else {
                    if (isStartAndPre) {
                        firstPath = Math.abs(connectPlanBO.getTargetNode().getX() - connectPlanBO.getPreConnect().getX()) + Math.abs(connectPlanBO.getTargetNode().getY() - connectPlanBO.getPreConnect().getY());
                        lastPath = Math.abs(connectPlanBO.getSufNodeConnect().getX() - targetNode.getX()) + Math.abs(connectPlanBO.getSufNodeConnect().getY() - targetNode.getY());
                    } else {
                        firstPath = Math.abs(targetNode.getX() - connectPlanBO.getPreConnect().getX()) + Math.abs(targetNode.getY() - connectPlanBO.getPreConnect().getY());
                        lastPath = Math.abs(connectPlanBO.getSufNodeConnect().getX() - connectPlanBO.getTargetNode().getX()) + Math.abs(connectPlanBO.getSufNodeConnect().getY() - connectPlanBO.getTargetNode().getY());
                    }
                    taskCount = taskService.countTaskByEndStr(connectPlanBO.getPreConnect().toString());
                }
            } catch (Exception e) {
                continue;
            }

            long pathCost = firstPath + lastPath + taskCount * 1000;
            if (minPathCost < 0) {
                minPathCost = pathCost;
                bestNodeIndex = i;
            }
            if (pathCost < minPathCost) {
                minPathCost = pathCost;
                bestNodeIndex = i;
            }
        }
        if (bestNodeIndex == -1) {
            return null;
        } else {
            return connectPlanBOS.get(bestNodeIndex);
        }
    }

    /**
     * 获取有效的接驳方案（提升机未禁用）
     *
     * @param connectConfDBS
     * @return
     */
    private List<ConnectConfDB> getAvailableConnectConfDB(List<ConnectConfDB> connectConfDBS) {
        if (CollectionUtils.isEmpty(connectConfDBS)) {
            return null;
        }

        Iterator<ConnectConfDB> itc = connectConfDBS.iterator();
        // 遍历接驳方案中的提升机，有禁用的移出接驳方案
        while (itc.hasNext()) {
            ConnectConfDB connectConfDB = itc.next();
            PlcDB plcDB = plcDBMapper.selectById(connectConfDB.getLifterId());
            if (plcDB == null || DeviceStatus.DISABLE.getCode().equals(plcDB.getStatus())) {
                itc.remove();
            }
        }

        return connectConfDBS;
    }

    /**
     * 修改跑码结果
     * @param coord
     * @param status : 跑码结果
     * @param calibrationTime : 校准时间
     * @param direction : 信息方向
     */
    public void updateRunForGCodeStatus(Coord coord, Integer status, Integer calibrationTime, NodeDirection direction) {
        UpdateWrapper<NodeDB> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(NodeDB::getGridx, coord.getX()).eq(NodeDB::getGridy, coord.getY())
                .eq(NodeDB::getGridz, coord.getZ());

        if (status != null) {
            switch (direction) {
                case UP:
                    updateWrapper.lambda().set(NodeDB::getRunUpForGroundCodeStatus, status);
                    break;
                case DOWN:
                    updateWrapper.lambda().set(NodeDB::getRunDownForGroundCodeStatus, status);
                    break;
                case LEFT:
                    updateWrapper.lambda().set(NodeDB::getRunLeftForGroundCodeStatus, status);
                    break;
                case RIGHT:
                    updateWrapper.lambda().set(NodeDB::getRunRightForGroundCodeStatus, status);
                    break;
                case ALL:
                    updateWrapper.lambda().set(NodeDB::getRunUpForGroundCodeStatus, status);
                    updateWrapper.lambda().set(NodeDB::getRunDownForGroundCodeStatus, status);
                    updateWrapper.lambda().set(NodeDB::getRunLeftForGroundCodeStatus, status);
                    updateWrapper.lambda().set(NodeDB::getRunRightForGroundCodeStatus, status);
                    break;
                default:
                    throw new RuntimeException(coord + "错误的NodeDirection:" + direction);
            }
        }

        if (calibrationTime != null) {
            switch (direction) {
                case UP:
                    updateWrapper.lambda().set(NodeDB::getUpCalibrationTime, calibrationTime);
                    break;
                case DOWN:
                    updateWrapper.lambda().set(NodeDB::getDownCalibrationTime, calibrationTime);
                    break;
                case LEFT:
                    updateWrapper.lambda().set(NodeDB::getLeftCalibrationTime, calibrationTime);
                    break;
                case RIGHT:
                    updateWrapper.lambda().set(NodeDB::getRightCalibrationTime, calibrationTime);
                    break;
                case ALL:
                    break;
                default:
                    throw new RuntimeException(coord + "错误的NodeDirection:" + direction);
            }
        }

        boolean result = this.update(updateWrapper);
        if (result) {
            NodeDB nodedb = getNodeDB(coord);
            NodeInfo nodeInfo = new NodeInfo();
            BeanUtils.copyProperties(nodedb, nodeInfo);
            NodeInfoPush.push(nodeInfo);
        }
    }

    /**
     * 节点个数>=2个
     * 点位必须在一条直线上
     * @param nodeList
     * @return
     */
    @Override
    public List<Node> findNodes(Set<Coord> nodeList) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        if (nodeList.size() < 2) {
            return new ArrayList<>();
        }
        List<Coord> coords = new ArrayList<>(nodeList);
        queryWrapper.lambda().eq(NodeDB::getGridz, coords.get(0).getZ());
        if (coords.get(1).getX() == coords.get(0).getX()) {
            queryWrapper.lambda().eq(NodeDB::getGridx, coords.get(0).getX());
        } else if (coords.get(1).getY() == coords.get(0).getY()) {
            queryWrapper.lambda().eq(NodeDB::getGridy, coords.get(0).getY());
        } else {
            throw new RuntimeException("点位不在同一条直线上");
        }
        List<NodeDB> nodeDBs = this.list(queryWrapper);
        List<Node> nodes = new ArrayList<>();
        nodeList.forEach(node -> {
            nodeDBs.forEach(nodeDB -> {
                Node temp = new Node(nodeDB.getGridx(), nodeDB.getGridy(), nodeDB.getGridz());
                if (node.equals(temp)) {
                    temp.setOffsetX(nodeDB.getOffsetx());
                    temp.setOffsetY(nodeDB.getOffsety());
                    temp.setRgv60Point(nodeDB.getRgvPoint());
                    temp.setGroundCode(nodeDB.getTag());
                    temp.setSonRgvGroundCode(nodeDB.getGroundCode());
                    temp.setNodeType(nodeDB.getGridType());
                    temp.setUp(nodeDB.getCanToUp());
                    temp.setDown(nodeDB.getCanToDown());
                    temp.setLeft(nodeDB.getCanToLeft());
                    temp.setRight(nodeDB.getCanToRight());
                    temp.setNodeStatus(NodeStatus.parse(nodeDB.getGridStatus()));
                    nodes.add(temp);
                }
            });
        });

        return nodes;
    }

    /**
     * 节点个数>=2个
     * 点位必须在一条直线上
     * @param coords
     * @return
     */
    @Override
    public List<Node> findNodes(List<Node> coords) {
        QueryWrapper<NodeDB> queryWrapper = new QueryWrapper<>();
        if (coords.size() < 2) {
            return new ArrayList<>();
        }
        queryWrapper.lambda().eq(NodeDB::getGridz, coords.get(0).getZ());
        if (coords.get(1).getX() == coords.get(0).getX()) {
            queryWrapper.lambda().eq(NodeDB::getGridx, coords.get(0).getX());
        } else if (coords.get(1).getY() == coords.get(0).getY()) {
            queryWrapper.lambda().eq(NodeDB::getGridy, coords.get(0).getY());
        } else {
            throw new RuntimeException("点位不在同一条直线上");
        }
        List<NodeDB> nodeDBs = this.list(queryWrapper);
        List<Node> nodes = new ArrayList<>();
        coords.forEach(node -> {
            nodeDBs.forEach(nodeDB -> {
                Node temp = new Node(nodeDB.getGridx(), nodeDB.getGridy(), nodeDB.getGridz());
                if (node.equals(temp)) {
                    temp.setOffsetX(nodeDB.getOffsetx());
                    temp.setOffsetY(nodeDB.getOffsety());
                    temp.setRgv60Point(nodeDB.getRgvPoint());
                    temp.setGroundCode(nodeDB.getTag());
                    temp.setSonRgvGroundCode(nodeDB.getGroundCode());
                    temp.setNodeType(nodeDB.getGridType());
                    temp.setUp(nodeDB.getCanToUp());
                    temp.setDown(nodeDB.getCanToDown());
                    temp.setLeft(nodeDB.getCanToLeft());
                    temp.setRight(nodeDB.getCanToRight());
                    nodes.add(temp);
                }
            });
        });

        return nodes;
    }

    @Override
    public void updateBatchByTag(NodeDB nodeDB) {
        LambdaUpdateWrapper<NodeDB> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(NodeDB::getOffsetx,nodeDB.getOffsetx()).
                set(NodeDB::getOffsety,nodeDB.getOffsety()).
                eq(NodeDB::getTag,nodeDB.getTag());
        baseMapper.update(null,lambdaUpdateWrapper);
    }

    public boolean addForbidRgv(Node node, Integer rgvType) {
        node = getNode(node);
        if (node.getForbidRgvs().contains(rgvType)) {
            return true;
        }

        node.addForbidRgv(rgvType);
        UpdateWrapper<NodeDB> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(NodeDB::getGridx, node.getX()).eq(NodeDB::getGridy, node.getY()).eq(NodeDB::getGridz,
                node.getZ()).set(NodeDB::getForbidRgvs, node.analyzeForbidRgvStr());
        return this.update(updateWrapper);
    }

    public boolean removeForbidRgv(Node node, Integer rgvType) {
        node = getNode(node);
        if (!node.getForbidRgvs().contains(rgvType)) {
            return true;
        }

        node.removeForbidRgv(rgvType);
        UpdateWrapper<NodeDB> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(NodeDB::getGridx, node.getX()).eq(NodeDB::getGridy, node.getY()).eq(NodeDB::getGridz,
                node.getZ()).set(NodeDB::getForbidRgvs, node.analyzeForbidRgvStr());
        return this.update(updateWrapper);
    }
}
